import 'dart:typed_data';
import 'package:binary/binary.dart';
import 'package:bluetooth_protocols_plugin/src/base/message/message_base.dart';
import 'package:bluetooth_protocols_plugin/src/tools/enums.dart';
import 'package:bluetooth_protocols_plugin/src/lorin/base/lorin_constant.dart';
import 'package:bluetooth_protocols_plugin/src/base/enum.dart';
import 'package:bluetooth_protocols_plugin/src/lorin/base/lorin_data.dart';
import 'package:bluetooth_protocols_plugin/src/lorin/base/lorin_tab.dart';
import 'package:bluetooth_protocols_plugin/src/tools/utils.dart';

import 'lorin_request_body.dart';

// 消息
class LorinMessage extends MessageBase {
  // 消息头
  static final Uint8List head =
      Uint8List.fromList([LorinConstant.HEAD1, LorinConstant.HEAD2]);

  // 帧主体：bit7：固定为0，bit6是否包含Tab，bit5-4校验方式，bit3是否加密，bit2-1-0数据格式
  late Uint8 format;

  // 是否需要加密
  late bool needCrypto;

  // format中bit3=1表示存在：每次通信随机一个0~255的值，作为密钥索引，报文中“Tab”开始到最后一字节的校验值都需要加密
  late Uint8? key;

  // 是否需要Tab
  late bool needTab;

  // format中bit6=1表示存在：包标签：在一次通讯过程中，它由主动发起数据请求的一方生成，接收方收到后如需应答则应答的数据帧必须携带同样的标签
  late LorinTab? tab;

  // data字段长度
  late Uint8 length;

  // 数据格式
  late LorinDataFormatType dataFormatType;

  // 数据段
  late LorinData? data;

  // 校验方式  0：none 无校验、1：parity 奇偶校验、2：crc16 CRC16_MODBUS标准计算、3：crc32 //CRC32计算
  late MessageCheckType checkType;

  // format中bit3=1表示存在：校验值(format-check的校验）
  late Uint8List? check;

  // 从LorinIntructionEnum中解析 LorinMessage
  LorinMessage.from(LorinRequestBody requestBody) {
    // 数据格式
    dataFormatType = requestBody.dataFormat!;
    // 数据段
    data = LorinData.from(requestBody);
    // 是否需要加密
    needCrypto = requestBody.needCrypto;
    // 随机数
    if (needCrypto) key = Uint8(Utils.random(0, 255));
    // tab标签
    needTab = requestBody.needTab;
    if (needTab) {
      tab = LorinTab(Utils.encodeTab(msgId));
      super.upMsgId();
    }
    // 数据段长度
    length = Uint8(data?.dataLength() ?? 0);
    // 校验类型
    checkType = MessageCheckType.parity;
    // 计算format
    format = getFormat();
    // 计算check
    check = checkType.checkBytes(_getNeedCheckList());
  }

  // 从Uint8List中解析 LorinMessage
  LorinMessage.fromList(Uint8List uint8list) {
    int offset = 0;
    if (uint8list.length >= minLength &&
        Utils.equalUint8Lists(
            uint8list.sublist(offset, offset + head.length), head)) {
      offset += 2;
      format = Uint8(uint8list.sublist(offset, offset + 1).first);
      offset += 1;
      // 是否加密
      needCrypto = _rspNeedCrypto();
      // 是否有Tab
      needTab = _rspNeedTab();
      // 校验方式
      checkType = _rspCheckType();
      // 数据段格式（标准组/专用组）
      dataFormatType = _rspDataFormatType();
      // 如果加密，获取随机值（目前默认没有加密）
      if (needCrypto) {
        key = Uint8(uint8list.sublist(offset, offset + 1).first);
        offset += 1;
      }
      tab = LorinTab(Utils.decodeTab2Uint8List(uint8list.sublist(offset)));
      if (needTab) {
        offset += tab!.dataLength();
      }
      // 数据段长度
      length = Uint8(uint8list.sublist(offset, offset + 1).first);
      offset += 1;
      // 数据段
      data = LorinData.fromList(
          dataFormatType, uint8list.sublist(offset, offset + length.value));

      offset += length.value;
      // 提取check
      check = uint8list.sublist(offset, offset + checkType.checkLength());
      if (!checkResult()) {
        throw Exception(
            "【$runtimeType】🟥 Check error: ${uint8list.toString()}");
      }
    } else {
      throw Exception("【$runtimeType】 Format error");
    }
  }

  @override
  String msgIdentify() {
    return Utils.computeDataIntVal(tab?.toBytes() ?? Uint8List(0)).toString();
  }

  @override
  Uint8List toBytes() {
    final builder = BytesBuilder();
    builder.add(head);
    builder.addByte(format.value);
    if (needCrypto) builder.addByte(key!.value);
    var tabBytes = needTab ? tab?.toBytes() : null;
    if (tabBytes != null) builder.add(tabBytes);
    builder.addByte(length.value);
    var dataBytes = data?.toBytes();
    if (dataBytes != null) builder.add(dataBytes);
    if (check != null && checkType != MessageCheckType.none) {
      builder.add(check!);
    }
    return builder.toBytes();
  }

  @override
  int totalLength() {
    return toBytes().length;
  }

  @override
  bool checkResult() {
    Uint8List calCheck = checkType.checkBytes(_getNeedCheckList());
    // 返回数据中的获取check值
    if (check == null) {
      return calCheck.isEmpty ? true : false;
    } else {
      return Utils.equalUint8Lists(check!, calCheck);
    }
  }

  String toMap() {
    return {
      "format": format.value,
      "needCrypto": needCrypto,
      "needTab": needTab,
      "tab": tab == null ? "" : tab?.toBytes(),
      "length": length.value,
      "dataFormatType": dataFormatType.rawValue,
      "data": data == null ? "" : data?.toBytes(),
      "checkType": checkType.rawValue,
      "check": check
    }.toString();
  }

  // 最小长度
  int get minLength => 4;

  // 提取Format
  Uint8 getFormat() {
    // 计算format
    int temp = 0;
    if (needTab) temp += (needTab ? 1 : 0) << 6;
    temp += (checkType.rawValue >> 1 & 0x01) << 5;
    temp += (checkType.rawValue & 0x01) << 4;
    if (needCrypto) temp += (needCrypto ? 1 : 0) << 3;
    temp += (dataFormatType.rawValue >> 2 & 0x01) << 2;
    temp += (dataFormatType.rawValue >> 1 & 0x01) << 1;
    temp += (dataFormatType.rawValue & 0x01);
    return Uint8(temp);
  }

  // 是否需要加密
  bool _rspNeedCrypto() {
    return (format.value >> 3 & 0x01) == 1;
  }

  // 是否需要Tab(由format中bit6决定)
  bool _rspNeedTab() {
    return (format.value >> 6 & 0x01) == 1;
  }

  // 校验方式(由format中bit5-4决定)
  MessageCheckType _rspCheckType() {
    return MessageCheckType.getTypeByNum(format.value >> 4 & 0x03) ??
        MessageCheckType.none;
  }

  // 数据格式
  LorinDataFormatType _rspDataFormatType() {
    return LorinDataFormatType.getTypeByNum(format.value & 0x07) ??
        LorinDataFormatType.none;
  }

  // 需要计算check的数组
  Uint8List _getNeedCheckList() {
    List<int> checkList = [];
    checkList.add(format.value);
    if (needCrypto && key != null) checkList.add(key!.value);
    if (needTab) {
      var tabBytes = tab?.toBytes();
      if (tabBytes != null) checkList.addAll(tabBytes);
    }
    checkList.add(length.value);
    var dataBytes = data?.toBytes();
    if (data != null && dataBytes != null) checkList.addAll(dataBytes);
    return Uint8List.fromList(checkList);
  }
}
